射線 Raycast 在Unity 中運用很廣泛,在射擊遊戲中常見,任何瞄準、理解當前物件的轉動方向的使用也很普遍。也因為最近在實驗室要使用到 Oculus進行研究,研究上某一小部分要知道當前 HMD 轉動的方向與角度是多少,故需要給定一個目標來給使用者瞄準,並衡量使用者頭部的轉動量。所以需要知道目前使用者是否瞄準目標的方式就是透過 Raycast 來觸發目標物件上的 Collider ,來確定是否視角有位移到該目標,並計數停留在目標持續的時間。
所以這邊我想簡單介紹該 Raycast 的使用。
接著新增一個文本 RaycastTest 到我們的 User_Sphere 中。
接下來就是撰寫當 User 的視線,也就是 Raycast 觸發到某一個 Collider 的時候回報給User。首先我們要先準備 Ray 也就是我們的射線。transform.position為我文本上物件的位置, Vector3.forward 為 new Vector3(0, 0, 1) 為物件前方的意思(z軸為物件的前方)。
Ray ray = new Ray(transform.position, Vector3.forward);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastTest : MonoBehaviour
{
void Update()
{
Ray ray = new Ray(transform.position, Vector3.forward);
if(Physics.Raycast(ray))
{
Debug.Log("The Ray hit something!");
}
}
}
RaycastHit hit;
修改一下程式碼後得到
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastTest : MonoBehaviour
{
void Update()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, Vector3.forward);
if(Physics.Raycast(ray, out hit))
{
Debug.Log(hit.transform.name + " hit by the ray!");
}
}
}
首先新增一個小 Sphere 。取名為 TargetPoint。 (p.s 可以透過按鍵 F 來追蹤在Scene該物件的位置)
我們也可透過 Raycast 的物件獲取該User 與 Target 的距離。
Debug.Log("Distance" + hit.distance);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastTest : MonoBehaviour
{
public GameObject targetPoint; // here the target point generate
void Awake() {
targetPoint.SetActive(false);
}
void Update()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, Vector3.forward);
if(Physics.Raycast(ray, out hit))
{
Debug.Log(hit.transform.name + " hit by the ray!");
Debug.Log("Distance" + hit.distance);
targetPoint.SetActive(true);
targetPoint.transform.position = hit.point;
}
else{
targetPoint.SetActive(false);
}
}
}
回到 Unity 將該 TargetPoint 拉近到我們的Script 文本中 GameObject 的位置。 並且務必記得要將TargetPoint 的Sphere Collider 取消勾選,否則 Ray會錯誤的一直去觸發 TargetPoint 的 Collider 而非我們要 Targtet的物件(TargetPoint後面的物件)。
執行後就會看到該小點在Target 上面。
首先我們知道每個物件會有個 Layer 的設定。我們依照 Layer 來確認該物件是否被 Ray 觸發。
接著修改我們觸發的判斷式,可以發現到 LayerMask 傳入的數值是 int,所以宣告的部分如下。
int layerMask = LayerMask.GetMask("Default");
if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask))
{
Debug.Log(hit.transform.name + " hit by the ray!");
Debug.Log("Distance" + hit.distance);
targetPoint.SetActive(true);
targetPoint.transform.position = hit.point;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RaycastTest : MonoBehaviour
{
public GameObject targetPoint; // here the target point generate
void Awake() {
targetPoint.SetActive(false);
}
void Update()
{
RaycastHit hit;
Ray ray = new Ray(transform.position, Vector3.forward);
int layerMask = LayerMask.GetMask("Default");
if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask, QueryTriggerInteraction.UseGlobal))
{
Debug.Log(hit.transform.name + " hit by the ray!");
Debug.Log("Distance" + hit.distance);
targetPoint.SetActive(true);
targetPoint.transform.position = hit.point;
}
else{
targetPoint.SetActive(false);
}
}
}
點選 Edit Collider 後就會看到Scene 出現一個綠色的框,也就是我們Collider 的樣貌。
但是這樣會讓 Ray 會去觸發到 Collider 的物件上,變成以下的樣子,這樣很怪有被空氣牆擋住的樣子。
這邊我們將剛剛的判斷式去做修改,主要修改 QueryTriggerInteraction.UseGlobal 的位置上。
if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask, QueryTriggerInteraction.UseGlobal))
{
Debug.Log(hit.transform.name + " hit by the ray!");
Debug.Log("Distance" + hit.distance);
targetPoint.SetActive(true);
targetPoint.transform.position = hit.point;
}
變成以下結果:
if(Physics.Raycast(ray, out hit, Mathf.Infinity, layerMask, QueryTriggerInteraction.Ignore))
{
Debug.Log(hit.transform.name + " hit by the ray!");
Debug.Log("Distance" + hit.distance);
targetPoint.SetActive(true);
targetPoint.transform.position = hit.point;
}